package com.oneandone.httpserver.tcp.http.request;

import java.io.IOException;
import java.io.InputStream;

import org.apache.log4j.Logger;

/**
 * @author calin
 */
public class BasicHttpRequestParser implements HttpRequestParser {
	private static final Logger LOGGER = Logger.getLogger(BasicHttpRequestParser.class);
	private String LF = "\n";
	private String CRLF = "\r\n";
	private String splitRegex;
    
	@Override
    public HttpRequest parse(InputStream stream) throws BadRequestException{
        LOGGER.debug("Reading the message");
		//Reading the message
    	byte[] buffer = new byte[1024];
        String msg = new String();
        try {
        	while (!msg.contains(CRLF + CRLF) && !msg.contains(LF + LF)){
        		int length = stream.read(buffer);
				msg += new String(buffer, 0,length);
        	}
        	 //Checking if message has LF or CRLF
            if (msg.contains(CRLF)){
            	splitRegex = CRLF;
            }
            else{
            	splitRegex = LF;
            }
            //Get Content-Length header
            int contentLength = getContentLenght(msg);
            if (msg.split(splitRegex + splitRegex).length > 1){
            	contentLength -= msg.split(splitRegex + splitRegex)[1].length();
            }
        	while (contentLength > 0){
        		int length = stream.read(buffer);
				msg += new String(buffer, 0,length);
				contentLength -= length;
        	}
        	
        } catch (IOException e) {
			LOGGER.error("Exception when reading the HTTP request", e);
		} 
        
        LOGGER.debug("Parsing the HTTP request");
        
        HttpRequest httpReq = new HttpRequest();
        //Checking if request has body
        String[] msgPiece = msg.split(splitRegex + splitRegex);
        if (msgPiece.length > 1){
        	httpReq.setBody(msgPiece[1].getBytes());
        }
        //Splitting the first piece of msg in lines
        String[] lines = msgPiece[0].split(splitRegex);
        
        //Analyzing the first line for method,version etc.
        String[] words = lines[0].split(" ");
        httpReq.setMethod(HttpMethod.valueOf(words[0]));
        httpReq.setUrl(words[1]);
        httpReq.setVersion(HttpVersion.getVersion(words[2]));
        
        //Parsing the rest of the lines for headers
        for (int i = 1; i < lines.length; i++){
        	words = lines[i].split(": ");
        	String value = "";
        	for (int j = 1; j < words.length; j++)
        		value += words[j];
        	httpReq.header(words[0], value);
        }
        
        //Getting the parameters
        if (httpReq.getUrl().split("\\?").length > 1){
        	getParam(httpReq,httpReq.getUrl().split("\\?")[1]);
        }
        if (httpReq.getMethod().equals(HttpMethod.POST) && httpReq.getBody() != null){
        	String body = new String(httpReq.getBody());
        	if (body.length() > 0){
        		getParam(httpReq , body);
        	}
        }
        
        //Checking the HTTP request
        if (httpReq.getVersion().equals(HttpVersion.HTTP11)){
			if (httpReq.getHeaders() == null || !httpReq.getHeaders().containsKey("host")){
				throw new BadRequestException();
			}
		}
        
        LOGGER.debug("HTTP request parsed successfully:\n" + httpReq);
        
        return httpReq;
        
    }
    
    public void getParam(HttpRequest httpReq , String msg) {
    	String[] paramList = msg.split("&");
		for (String param : paramList) {
			String name = param.split("=")[0];
			String value = param.split("=")[1];
			if (name.contains("+")) {
				for (String strName : name.split("\\+")) {
					if (value.contains("+")) {
						for (String strValue : value.split("\\+")) {
							httpReq.param(strName, strValue);
						}
					} else {
						httpReq.param(strName, value);
					}
				}
			} else {
				if (value.contains("+")) {
					for (String strValue : value.split("\\+")) {
						httpReq.param(name, strValue);
					}
				} else {
					httpReq.param(name,value);
				}
			}
		}
	}
    
    public int getContentLenght(String msg){
        String headers[] = msg.split(splitRegex);
        for (String header : headers){
        	if (header.matches("Content-Length")){
        		return Integer.parseInt(header.split(": ")[1]);
        	}
        }
        return 0;
    	
    }
}
